/**
 * @file mainwindow.cpp
 * @brief Main Qt file for the graphical user interface code
 * @author Riccardo Di Maio
 * @version 1.0 15/03/19
 */

// VTK libraries
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkCamera.h>
#include <vtkPolyData.h>
#include <vtkDataSetMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPlane.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkLight.h>
#include <vtkAxesActor.h>
#include <vtkTransform.h>

// VTK libraries - cells
#include <vtkCellArray.h>
#include <vtkCellType.h>
#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkTetra.h>
#include <vtkPyramid.h>
#include <vtkHexahedron.h>
#include <vtkMassProperties.h>

// VTK libraries - filters
#include <vtkTriangleFilter.h>
#include <vtkShrinkFilter.h>
#include <vtkClipDataSet.h>

// VTK libraries - STL reading
#include <vtkSTLReader.h>
#include <vtkPolyDataMapper.h>

// VTK libraries - 截图功能
#include <vtkWindowToImageFilter.h>
#include <vtkPNGWriter.h>

// Qt headers
#include <QDebug>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "helpdialog.h"
#include "clipdialog.h"

// Local headers
#include "model/model.h"

// VTK全局变量
// 创建VTK渲染窗口和渲染器
vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
// 创建STL读取器
vtkSmartPointer<vtkSTLReader> reader;
// 创建颜色对象
vtkSmartPointer<vtkNamedColors> colors = vtkSmartPointer<vtkNamedColors>::New();
// 设置光源
vtkSmartPointer<vtkLight> light = vtkSmartPointer<vtkLight>::New();
// 设置坐标轴
vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
// 设置裁剪平面
vtkSmartPointer<vtkClipDataSet> clipFilter;
vtkSmartPointer<vtkPlane> clipPlane;
float clipX = 0;
float clipY = 0;
float clipZ = 0;
float clipNormalX = -1;
float clipNormalY = 0;
float clipNormalZ = 0;
float prevClipX = 0;
float prevClipY = 0;
float prevClipZ = 0;
float prevClipNormalX = -1;
float prevClipNormalY = 0;
float prevClipNormalZ = 0;
// 初始化mapper和actor向量
// .mod文件每个单元格都有一个actor/mapper,
// 而.stl文件整个文件只需要一个actor/mapper
std::vector<vtkSmartPointer<vtkDataSetMapper>> mappers;
std::vector<vtkSmartPointer<vtkActor>> actors;
// 初始化.mod文件解析向量
std::vector<vtkSmartPointer<vtkUnstructuredGrid>> unstructuredGrids;
std::vector<vtkSmartPointer<vtkTetra>> tetras;
std::vector<vtkSmartPointer<vtkPyramid>> pyras;
std::vector<vtkSmartPointer<vtkHexahedron>> hexas;
QString inputFileName;     // 模型文件名的全局字符串
bool modelLoaded = false;  // 表示当前是否已加载模型的全局标志
bool clipFilterEnabled = false;
bool clipWindowShown = false;

// 声明统计信息QString(作为全局字符串用于导出数据功能)
QString surfAreaString;
QString volumeString;
QString cellString;
QString pointString;

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    clipWindow = new ClipDialog();

    this->setupWindow();

    // 设置光源(但不添加到场景中)
    light->SetLightTypeToCameraLight();
    light->SetIntensity(0.5);
    light->SetFocalPoint(1.875, 0.6125, 0);
    light->SetPosition(0.875, 1.6125, 1);

    // 坐标轴通过用户变换进行定位
    transform->Translate(1.0, 0.0, 0.0);
    axes->SetUserTransform(transform);

    ui->intensitySlider->setEnabled(false);
    ui->shrinkButton->setEnabled(false);
    ui->clipButton->setEnabled(false);
    ui->resetFiltersButton->setEnabled(false);

    // 设置渲染器的相机
    renderer->ResetCamera();
    renderer->GetActiveCamera()->Azimuth(30);
    renderer->GetActiveCamera()->Elevation(30);
    renderer->ResetCameraClippingRange();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupWindow()
{
    // standard call to setup Qt UI (same as previously)
    ui->setupUi(this);

    setupButtons(modelLoaded);
    setupIcons();
    setupConnects();

    setWindowTitle(tr("VTK"));

    // Only enable STL and MOD tests if files are present
    std::string dataDir = DEFAULT_AO_DATA_DIR;

    std::ifstream modfile(dataDir + "/ExampleModel.mod");
    std::ifstream stlfile(dataDir + "/ExampleSTL.stl");
    if (!modfile.good())
    {
        ui->actionModTest->setEnabled(false);
    }
    if (!stlfile.good())
    {
        ui->actionStlTest->setEnabled(false);
    }

    // 将 VTK 渲染窗口与 Qt 小部件关联。Widget 是在 Qt Creator 中为 Qt Open Widget 所命名的名称。
    ui->qvtkWidget->setRenderWindow(renderWindow);
    // 将渲染器添加到渲染窗口
    ui->qvtkWidget->renderWindow()->AddRenderer(renderer);
}

void MainWindow::setupIcons()
{
    ui->actionOpen->setIcon(QIcon(":/open"));
    ui->actionSave->setIcon(QIcon(":/save"));
    ui->actionClose->setIcon(QIcon(":/close"));
    ui->actionPrint->setIcon(QIcon(":/print"));
    ui->actionExportData->setIcon(QIcon(":/data"));
    ui->actionAbout->setIcon(QIcon(":/info"));
    ui->actionStlTest->setIcon(QIcon(":/stltest"));
    ui->actionModTest->setIcon(QIcon(":/modtest"));
    ui->actionFullScreen->setIcon(QIcon(":/fullscreen"));
}

void MainWindow::setupButtons(bool modelLoaded)
{
    // 如果没有模型加载，则按钮变灰；如果模型已加载，则启用按钮
    ui->actionSave->setEnabled(modelLoaded);
    ui->actionClose->setEnabled(modelLoaded);
    ui->actionPrint->setEnabled(modelLoaded);
    ui->actionExportData->setEnabled(modelLoaded);
    ui->modColourButton->setEnabled(modelLoaded);
    ui->gradientCheckBox->setChecked(true);  // 初始化渐变背景开启
    ui->intensityCheckBox->setEnabled(modelLoaded);
    ui->intensityCheckBox->setChecked(false);            // 初始化外部光源关闭
    ui->surfaceRadio->setChecked(true);                  // 初始化表面可视化开启
    ui->resetCameraButton->setEnabled(modelLoaded);      // 重置相机按钮
    ui->resetPropertiesButton->setEnabled(modelLoaded);  // 重置属性按钮
    ui->resetLightingButton->setEnabled(modelLoaded);    // 重置光照按钮
    ui->opacitySlider->setEnabled(modelLoaded);          // 透明度滑块
    ui->opacitySlider->setValue(99);                     // 初始化滑块到最大值
    ui->specularitySlider->setEnabled(modelLoaded);      // 镜面反射滑块
    ui->specularitySlider->setValue(49);                 // 初始化滑块到中间值
    ui->intensitySlider->setValue(49);                   // 初始化滑块到中间值
    ui->posXButton->setEnabled(modelLoaded);             // X轴正方向视角按钮
    ui->posYButton->setEnabled(modelLoaded);             // Y轴正方向视角按钮
    ui->posZButton->setEnabled(modelLoaded);             // Z轴正方向视角按钮
    ui->pos90Button->setEnabled(modelLoaded);            // 正90度旋转按钮
    ui->negXButton->setEnabled(modelLoaded);             // X轴负方向视角按钮
    ui->negYButton->setEnabled(modelLoaded);             // Y轴负方向视角按钮
    ui->negZButton->setEnabled(modelLoaded);             // Z轴负方向视角按钮
    ui->neg90Button->setEnabled(modelLoaded);            // 负90度旋转按钮
    // 取消选中过滤器按钮
    ui->shrinkButton->setCheckable(true);  // 收缩过滤器按钮
    ui->clipButton->setCheckable(true);    // 裁剪过滤器按钮
    ui->shrinkButton->setChecked(false);
    ui->clipButton->setChecked(false);
}

void MainWindow::setupConnects()
{
    connect(this, &MainWindow::statusUpdateMessage, ui->statusBar, &QStatusBar::showMessage);
    connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(handleActionOpen()));
    connect(ui->actionSave, SIGNAL(triggered()), this, SLOT(handleActionSave()));
    connect(ui->actionClose, SIGNAL(triggered()), this, SLOT(handleActionClose()));
    connect(ui->actionPrint, SIGNAL(triggered()), this, SLOT(handleActionPrint()));
    connect(ui->actionFullScreen, SIGNAL(triggered()), this, SLOT(handleActionFullScreen()));
    connect(ui->actionExportData, SIGNAL(triggered()), this, SLOT(handleActionExportData()));
    connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(handleActionAbout()));
    connect(ui->actionResetFilters, SIGNAL(triggered()), this, SLOT(handleActionResetFilters()));
    connect(ui->actionResetCamera, SIGNAL(triggered()), this, SLOT(handleActionResetCamera()));
    connect(ui->actionResetLighting, SIGNAL(triggered()), this, SLOT(handleActionResetLighting()));
    connect(ui->actionResetProperties, SIGNAL(triggered()), this, SLOT(handleActionResetProperties()));
    connect(ui->actionStlTest, SIGNAL(triggered()), this, SLOT(handleActionStlTest()));
    connect(ui->actionModTest, SIGNAL(triggered()), this, SLOT(handleActionModTest()));
    connect(ui->actionShowAxes, SIGNAL(triggered()), this, SLOT(handleActionShowAxes()));
    connect(ui->actionEnableIntensity, SIGNAL(triggered()), this, SLOT(handleActionEnableIntensity()));
    connect(ui->gradientCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_gradientCheckBox_stateChanged(int)));
    connect(ui->intensityCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_intensityCheckBox_stateChanged(int)));
    connect(ui->showAxesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_showAxesCheckBox_stateChanged(int)));
    connect(ui->wireframeRadio, SIGNAL(toggled(bool)), this, SLOT(on_wireframeRadio_toggled(bool)));
    connect(ui->pointsRadio, SIGNAL(toggled(bool)), this, SLOT(on_pointsRadio_toggled(bool)));
    connect(ui->surfaceRadio, SIGNAL(toggled(bool)), this, SLOT(on_surfaceRadio_toggled(bool)));
    connect(ui->opacitySlider, SIGNAL(sliderMoved(int)), this, SLOT(on_opacitySlider_sliderMoved(int)));
    connect(ui->opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(on_opacitySlider_valueChanged(int)));
    connect(ui->specularitySlider, SIGNAL(sliderMoved(int)), this, SLOT(on_specularitySlider_sliderMoved(int)));
    connect(ui->specularitySlider, SIGNAL(valueChanged(int)), this, SLOT(on_specularitySlider_valueChanged(int)));
    connect(ui->intensitySlider, SIGNAL(sliderMoved(int)), this, SLOT(on_intensitySlider_sliderMoved(int)));
    connect(clipWindow, SIGNAL(xSliderMoved(int)), this, SLOT(on_clipXSlider_sliderMoved(int)));
    connect(clipWindow, SIGNAL(ySliderMoved(int)), this, SLOT(on_clipYSlider_sliderMoved(int)));
    connect(clipWindow, SIGNAL(zSliderMoved(int)), this, SLOT(on_clipZSlider_sliderMoved(int)));
    connect(clipWindow, SIGNAL(xDialMoved(int)), this, SLOT(on_clipXDial_sliderMoved(int)));
    connect(clipWindow, SIGNAL(yDialMoved(int)), this, SLOT(on_clipYDial_sliderMoved(int)));
    connect(clipWindow, SIGNAL(zDialMoved(int)), this, SLOT(on_clipZDial_sliderMoved(int)));
    connect(clipWindow, SIGNAL(clipDialogRejected()), this, SLOT(on_clipDialog_dialogRejected()));
    connect(clipWindow, SIGNAL(clipDialogAccepted()), this, SLOT(on_clipDialog_dialogAccepted()));
}

void MainWindow::loadModel(QString inputFilename)
{
    // 声明要在统计区域显示的数据
    double modSurfArea = 0;
    double modVolume = 0;
    int modCells = 0;
    int modPoints = 0;
    vtkSmartPointer<vtkPolyData> modPolyData;

    // 如果已经加载了模型，则初始化相关变量
    if (modelLoaded)
    {
        clearModel();
    }

    // 将QString转换为std::string
    std::string modelFileName = inputFilename.toUtf8().constData();

    // 检查文件是否存在
    QFileInfo fileInfo(inputFilename);
    if (!fileInfo.exists())
    {
        Q_EMIT statusUpdateMessage(QString("Error: File does not exist"), 0);
        return;
    }

    // 检查文件大小
    if (fileInfo.size() == 0)
    {
        Q_EMIT statusUpdateMessage(QString("Error: File is empty"), 0);
        return;
    }

    // 加载模型
    Model mod1(modelFileName);

    // 判断模型文件类型
    if (mod1.getIsSTL())
    {
        // STL文件处理
        Q_EMIT statusUpdateMessage(QString("Loading STL file..."), 0);

        // 允许用户选择仅适用于STL的过滤器
        ui->shrinkButton->setEnabled(true);
        ui->resetFiltersButton->setEnabled(true);
        ui->clipButton->setEnabled(true);

        // 调整actors和mappers大小以适应STL文件
        actors.resize(1);
        mappers.resize(1);

        // 创建STL读取器
        reader = vtkSmartPointer<vtkSTLReader>::New();
        reader->SetFileName(modelFileName.c_str());

        // 检查文件是否成功读取
        try
        {
            reader->Update();
        }
        catch (...)
        {
            Q_EMIT statusUpdateMessage(QString("Error: Failed to read STL file"), 0);
            modelLoaded = false;
            return;
        }

        // 检查是否有有效数据
        if (reader->GetOutput()->GetNumberOfPoints() == 0)
        {
            Q_EMIT statusUpdateMessage(QString("Error: STL file contains no data"), 0);
            modelLoaded = false;
            return;
        }

        // 创建数据集映射器
        mappers[0] = vtkSmartPointer<vtkDataSetMapper>::New();
        mappers[0]->SetInputConnection(reader->GetOutputPort());

        // 创建演员
        actors[0] = vtkSmartPointer<vtkActor>::New();
        actors[0]->SetMapper(mappers[0]);
        actors[0]->GetProperty()->SetColor(colors->GetColor3d("Red").GetData());
        actors[0]->GetProperty()->SetSpecular(0.5);
        actors[0]->GetProperty()->SetSpecularPower(5);

        // 性能优化：对于大型STL文件使用平坦着色
        vtkSmartPointer<vtkPolyData> stlPolyData = reader->GetOutput();
        if (stlPolyData->GetNumberOfPolys() > 10000)
        {
            actors[0]->GetProperty()->SetInterpolationToFlat();
        }

        // 获取模型统计信息
        Q_EMIT statusUpdateMessage(QString("Calculating model statistics..."), 0);

        vtkSmartPointer<vtkTriangleFilter> triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New();
        vtkSmartPointer<vtkMassProperties> massProperty = vtkSmartPointer<vtkMassProperties>::New();

        triangleFilter->SetInputConnection(reader->GetOutputPort());
        triangleFilter->Update();

        // 检查三角化后的数据
        if (triangleFilter->GetOutput()->GetNumberOfPolys() == 0)
        {
            Q_EMIT statusUpdateMessage(QString("Warning: No polygons after triangulation"), 0);
            modSurfArea = 0.0;
            modVolume = 0.0;
        }
        else
        {
            massProperty->SetInputConnection(triangleFilter->GetOutputPort());
            massProperty->Update();

            // 获取统计信息
            modSurfArea = massProperty->GetSurfaceArea();
            modVolume = massProperty->GetVolume();

            // 验证计算结果
            if (modSurfArea < 0 || std::isnan(modSurfArea))
            {
                Q_EMIT statusUpdateMessage(QString("Warning: Invalid surface area calculated"), 0);
                modSurfArea = 0.0;
            }
            if (modVolume < 0 || std::isnan(modVolume))
            {
                Q_EMIT statusUpdateMessage(QString("Warning: Invalid volume calculated"), 0);
                modVolume = 0.0;
            }
        }

        modCells = stlPolyData->GetNumberOfPolys();
        modPoints = stlPolyData->GetNumberOfPoints();

        // 将演员添加到渲染器
        renderer->AddActor(actors[0]);
        Q_EMIT statusUpdateMessage(QString("Loaded STL model successfully"), 0);
    }
    else
    {
        // MOD文件处理
        Q_EMIT statusUpdateMessage(QString("Loading MOD file..."), 0);

        // 禁止用户选择仅适用于STL的过滤器
        ui->shrinkButton->setEnabled(false);
        ui->resetFiltersButton->setEnabled(false);
        ui->clipButton->setEnabled(false);

        // 从模型获取单元格向量和材料向量
        std::vector<Cell> modCellsVector = mod1.getCells();
        std::vector<Material> modMaterials = mod1.getMaterials();

        // 调整大小以适应新模型
        unstructuredGrids.resize(modCellsVector.size());
        actors.resize(modCellsVector.size());
        mappers.resize(modCellsVector.size());

        // 清理之前的资源
        unstructuredGrids.clear();
        actors.clear();
        mappers.clear();
        tetras.clear();
        pyras.clear();
        hexas.clear();

        // 重新调整大小
        unstructuredGrids.resize(modCellsVector.size());
        actors.resize(modCellsVector.size());
        mappers.resize(modCellsVector.size());

        // 创建用于统计的PolyData - 使用更可靠的方法
        vtkSmartPointer<vtkPolyData> totalPolyData = vtkSmartPointer<vtkPolyData>::New();
        vtkSmartPointer<vtkPoints> totalPoints = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkCellArray> totalCellArray = vtkSmartPointer<vtkCellArray>::New();

        int validCellCount = 0;
        int totalPointsCount = 0;

        // 遍历每个单元格
        for (int cellIndex = 0; cellIndex < modCellsVector.size(); ++cellIndex)
        {
            Cell currentCell = modCellsVector[cellIndex];
            std::vector<Vector3D> cellVertices = currentCell.getVertices();

            // 跳过无效的单元格
            if (cellVertices.empty())
            {
                continue;
            }

            // 为每个单元格创建独立的点集合和单元格数组
            vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
            vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();

            // 创建非结构化网格
            unstructuredGrids[cellIndex] = vtkSmartPointer<vtkUnstructuredGrid>::New();

            bool cellCreated = false;

            // 根据顶点数量确定单元格类型
            if (cellVertices.size() == 4)  // 四面体
            {
                // 添加顶点到点集合
                for (int i = 0; i < 4; i++)
                {
                    points->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                }

                // 创建四面体
                vtkSmartPointer<vtkTetra> tetra = vtkSmartPointer<vtkTetra>::New();
                for (int i = 0; i < 4; i++)
                {
                    tetra->GetPointIds()->SetId(i, i);
                }

                cellArray->InsertNextCell(tetra);
                unstructuredGrids[cellIndex]->SetPoints(points);
                unstructuredGrids[cellIndex]->SetCells(VTK_TETRA, cellArray);
                cellCreated = true;

                // 添加到总PolyData用于统计
                vtkSmartPointer<vtkTetra> statTetra = vtkSmartPointer<vtkTetra>::New();
                int pointOffset = totalPoints->GetNumberOfPoints();
                for (int i = 0; i < 4; i++)
                {
                    totalPoints->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                    statTetra->GetPointIds()->SetId(i, pointOffset + i);
                }
                totalCellArray->InsertNextCell(statTetra);
                totalPointsCount += 4;
            }
            else if (cellVertices.size() == 5)  // 金字塔
            {
                // 添加顶点到点集合
                for (int i = 0; i < 5; i++)
                {
                    points->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                }

                // 创建金字塔
                vtkSmartPointer<vtkPyramid> pyramid = vtkSmartPointer<vtkPyramid>::New();
                for (int i = 0; i < 5; i++)
                {
                    pyramid->GetPointIds()->SetId(i, i);
                }

                cellArray->InsertNextCell(pyramid);
                unstructuredGrids[cellIndex]->SetPoints(points);
                unstructuredGrids[cellIndex]->SetCells(VTK_PYRAMID, cellArray);
                cellCreated = true;

                // 金字塔转换为2个四面体进行统计计算
                int pointOffset = totalPoints->GetNumberOfPoints();
                for (int i = 0; i < 5; i++)
                {
                    totalPoints->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                }

                // 金字塔分解为四面体1: 0,1,2,4
                vtkSmartPointer<vtkTetra> tetra1 = vtkSmartPointer<vtkTetra>::New();
                tetra1->GetPointIds()->SetId(0, pointOffset + 0);
                tetra1->GetPointIds()->SetId(1, pointOffset + 1);
                tetra1->GetPointIds()->SetId(2, pointOffset + 2);
                tetra1->GetPointIds()->SetId(3, pointOffset + 4);
                totalCellArray->InsertNextCell(tetra1);

                // 金字塔分解为四面体2: 0,2,3,4
                vtkSmartPointer<vtkTetra> tetra2 = vtkSmartPointer<vtkTetra>::New();
                tetra2->GetPointIds()->SetId(0, pointOffset + 0);
                tetra2->GetPointIds()->SetId(1, pointOffset + 2);
                tetra2->GetPointIds()->SetId(2, pointOffset + 3);
                tetra2->GetPointIds()->SetId(3, pointOffset + 4);
                totalCellArray->InsertNextCell(tetra2);

                totalPointsCount += 5;
            }
            else if (cellVertices.size() == 8)  // 六面体
            {
                // 添加顶点到点集合
                for (int i = 0; i < 8; i++)
                {
                    points->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                }

                // 创建六面体
                vtkSmartPointer<vtkHexahedron> hexahedron = vtkSmartPointer<vtkHexahedron>::New();
                for (int i = 0; i < 8; i++)
                {
                    hexahedron->GetPointIds()->SetId(i, i);
                }

                cellArray->InsertNextCell(hexahedron);
                unstructuredGrids[cellIndex]->SetPoints(points);
                unstructuredGrids[cellIndex]->SetCells(VTK_HEXAHEDRON, cellArray);
                cellCreated = true;

                // 六面体转换为5个四面体进行统计计算
                int pointOffset = totalPoints->GetNumberOfPoints();
                for (int i = 0; i < 8; i++)
                {
                    totalPoints->InsertNextPoint(cellVertices[i].getX(), cellVertices[i].getY(), cellVertices[i].getZ());
                }

                // 六面体分解为5个四面体（标准分解方法）
                // 四面体1: 0,1,3,4
                vtkSmartPointer<vtkTetra> tetra1 = vtkSmartPointer<vtkTetra>::New();
                tetra1->GetPointIds()->SetId(0, pointOffset + 0);
                tetra1->GetPointIds()->SetId(1, pointOffset + 1);
                tetra1->GetPointIds()->SetId(2, pointOffset + 3);
                tetra1->GetPointIds()->SetId(3, pointOffset + 4);
                totalCellArray->InsertNextCell(tetra1);

                // 四面体2: 1,2,3,6
                vtkSmartPointer<vtkTetra> tetra2 = vtkSmartPointer<vtkTetra>::New();
                tetra2->GetPointIds()->SetId(0, pointOffset + 1);
                tetra2->GetPointIds()->SetId(1, pointOffset + 2);
                tetra2->GetPointIds()->SetId(2, pointOffset + 3);
                tetra2->GetPointIds()->SetId(3, pointOffset + 6);
                totalCellArray->InsertNextCell(tetra2);

                // 四面体3: 1,3,4,6
                vtkSmartPointer<vtkTetra> tetra3 = vtkSmartPointer<vtkTetra>::New();
                tetra3->GetPointIds()->SetId(0, pointOffset + 1);
                tetra3->GetPointIds()->SetId(1, pointOffset + 3);
                tetra3->GetPointIds()->SetId(2, pointOffset + 4);
                tetra3->GetPointIds()->SetId(3, pointOffset + 6);
                totalCellArray->InsertNextCell(tetra3);

                // 四面体4: 4,5,6,1
                vtkSmartPointer<vtkTetra> tetra4 = vtkSmartPointer<vtkTetra>::New();
                tetra4->GetPointIds()->SetId(0, pointOffset + 4);
                tetra4->GetPointIds()->SetId(1, pointOffset + 5);
                tetra4->GetPointIds()->SetId(2, pointOffset + 6);
                tetra4->GetPointIds()->SetId(3, pointOffset + 1);
                totalCellArray->InsertNextCell(tetra4);

                // 四面体5: 4,6,7,5
                vtkSmartPointer<vtkTetra> tetra5 = vtkSmartPointer<vtkTetra>::New();
                tetra5->GetPointIds()->SetId(0, pointOffset + 4);
                tetra5->GetPointIds()->SetId(1, pointOffset + 6);
                tetra5->GetPointIds()->SetId(2, pointOffset + 7);
                tetra5->GetPointIds()->SetId(3, pointOffset + 5);
                totalCellArray->InsertNextCell(tetra5);

                totalPointsCount += 8;
            }

            if (cellCreated)
            {
                // 创建映射器和演员
                mappers[cellIndex] = vtkSmartPointer<vtkDataSetMapper>::New();
                mappers[cellIndex]->SetInputData(unstructuredGrids[cellIndex]);

                actors[cellIndex] = vtkSmartPointer<vtkActor>::New();
                actors[cellIndex]->SetMapper(mappers[cellIndex]);

                // 设置颜色
                Material mat = currentCell.getMaterial();
                std::string matColour = mat.getColour();
                const char* rgbColour = matColour.c_str();
                int r, g, b;

                if (sscanf_s(rgbColour, "%02x%02x%02x", &r, &g, &b) == 3)
                {
                    double dr = (double)r / 255;
                    double dg = (double)g / 255;
                    double db = (double)b / 255;
                    actors[cellIndex]->GetProperty()->SetColor(dr, dg, db);
                }
                else
                {
                    // 使用默认颜色
                    actors[cellIndex]->GetProperty()->SetColor(1.0, 0.0, 0.0);
                }

                actors[cellIndex]->GetProperty()->SetSpecular(0.5);
                actors[cellIndex]->GetProperty()->SetSpecularPower(5);

                // 将演员添加到渲染器
                renderer->AddActor(actors[cellIndex]);
                validCellCount++;
            }
        }

        // 计算MOD模型的统计信息
        Q_EMIT statusUpdateMessage(QString("Calculating MOD model statistics..."), 0);

        // 设置PolyData
        totalPolyData->SetPoints(totalPoints);
        totalPolyData->SetPolys(totalCellArray);

        // 调试信息
        qDebug() << "Statistics calculation - Points:" << totalPoints->GetNumberOfPoints() << "Cells:" << totalCellArray->GetNumberOfCells();

        // 检查是否有有效数据
        if (totalPoints->GetNumberOfPoints() == 0 || totalCellArray->GetNumberOfCells() == 0)
        {
            Q_EMIT statusUpdateMessage(QString("Warning: No valid geometry data for statistics"), 0);
            modSurfArea = 0.0;
            modVolume = 0.0;
        }
        else
        {
            vtkSmartPointer<vtkTriangleFilter> triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New();
            vtkSmartPointer<vtkMassProperties> massProperty = vtkSmartPointer<vtkMassProperties>::New();

            triangleFilter->SetInputData(totalPolyData);

            try
            {
                triangleFilter->Update();

                // 检查三角化后的输出
                vtkPolyData* triangleOutput = triangleFilter->GetOutput();
                if (triangleOutput && triangleOutput->GetNumberOfPolys() > 0)
                {
                    massProperty->SetInputConnection(triangleFilter->GetOutputPort());
                    massProperty->Update();

                    modSurfArea = massProperty->GetSurfaceArea();
                    modVolume = massProperty->GetVolume();

                    // 验证计算结果
                    if (modSurfArea < 0 || std::isnan(modSurfArea))
                    {
                        Q_EMIT statusUpdateMessage(QString("Warning: Invalid surface area calculated"), 0);
                        modSurfArea = 0.0;
                    }
                    if (modVolume < 0 || std::isnan(modVolume))
                    {
                        Q_EMIT statusUpdateMessage(QString("Warning: Invalid volume calculated"), 0);
                        modVolume = 0.0;
                    }
                }
                else
                {
                    Q_EMIT statusUpdateMessage(QString("Warning: No polygons after triangulation"), 0);
                    modSurfArea = 0.0;
                    modVolume = 0.0;
                }
            }
            catch (...)
            {
                Q_EMIT statusUpdateMessage(QString("Error during statistics calculation"), 0);
                modSurfArea = 0.0;
                modVolume = 0.0;
            }
        }

        modCells = validCellCount;
        modPoints = mod1.getVertexCount();

        Q_EMIT statusUpdateMessage(QString("Loaded MOD model successfully"), 0);
    }

    // 将标志设置回true
    modelLoaded = true;

    // 启用与模型查看相关的按钮
    setupButtons(modelLoaded);
    resetCamera();

    ui->qvtkWidget->renderWindow()->Render();

    // 在统计区域显示统计数据
    surfAreaString = QString::number(modSurfArea, 'f', 6) + " m²";
    volumeString = QString::number(modVolume, 'f', 6) + " m³";
    cellString = QString::number(modCells);
    pointString = QString::number(modPoints);

    ui->surfAreaValue->setText(surfAreaString);
    ui->volValue->setText(volumeString);
    ui->cellsValue->setText(cellString);
    ui->pointsValue->setText(pointString);

    Q_EMIT statusUpdateMessage(QString("Model loaded successfully - Cells: %1, Points: %2").arg(cellString).arg(pointString), 5000);
}

void MainWindow::clearModel()
{
    // 保留背景颜色设置
    double r, g, b;
    double r2, g2, b2;
    renderer->GetBackground(r, g, b);
    renderer->GetBackground2(r2, g2, b2);
    bool isGradient = renderer->GetGradientBackground();

    // 从渲染窗口中移除渲染器
    ui->qvtkWidget->renderWindow()->RemoveRenderer(renderer);
    renderer = NULL;  // 通过将其赋值为NULL来释放之前的渲染器
    // 创建一个新的渲染器指针
    renderer = vtkSmartPointer<vtkRenderer>::New();
    // 重新将渲染器添加到渲染窗口
    ui->qvtkWidget->renderWindow()->AddRenderer(renderer);
    ui->qvtkWidget->renderWindow()->Render();

    // 设置之前的背景颜色
    renderer->SetBackground(r, g, b);
    renderer->SetBackground2(r2, g2, b2);

    // 如果之前启用了渐变背景，则重新启用
    if (isGradient)
    {
        renderer->GradientBackgroundOn();
    }

    // 清除统计信息文本
    ui->surfAreaValue->setText("");
    ui->volValue->setText("");
    ui->cellsValue->setText("");
    ui->pointsValue->setText("");

    // 禁用仅适用于STL文件的过滤器按钮
    ui->shrinkButton->setEnabled(false);
    ui->resetFiltersButton->setEnabled(false);
    ui->clipButton->setEnabled(false);

    // 设置模型加载标志为false
    modelLoaded = false;
    // 重新渲染窗口
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::resetCamera()
{
    // These lines are needed to ensure the button can be pressed more than
    // once per each model
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(0.201282, 0.526811, -0.299848);
    renderer->GetActiveCamera()->SetPosition(0.795337, -0.696117, -0.689135);

    // Reset camera and re-render scene
    renderer->ResetCamera();
}

void MainWindow::handleActionOpen()
{
    // Prompt user for a filename
    inputFileName = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath(), tr("Supported Models (*.mod *.stl);;STL Model (*.stl);;Proprietary Model (*.mod)"));

    if (!inputFileName.isEmpty() && !inputFileName.isNull())
    {
        if (modelLoaded)
        {
            clearModel();
        }
        loadModel(inputFileName);
    }
    else
    {
        Q_EMIT statusUpdateMessage(QString("Error while opening file"), 0);
    }
}

void MainWindow::handleActionSave()
{
    // Only save if a model is already loaded
    if (!modelLoaded)
    {
        Q_EMIT statusUpdateMessage(QString("Error: cannot save when a model is not loaded"), 0);
    }
    else
    {
        // Prompt user for a filename
        QString outputFileName = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::currentPath(), tr("Supported Models (*.mod *.stl);;STL Model (*.stl);;Proprietary Model (*.mod)"));

        if (!outputFileName.isEmpty() && !outputFileName.isNull())
        {
            if (!QFile::copy(inputFileName, outputFileName))
            {
                Q_EMIT statusUpdateMessage(QString("Error while saving file"), 0);
            }
        }
        else
        {
            Q_EMIT statusUpdateMessage(QString("Error: no save name inserted"), 0);
        }
    }
}

void MainWindow::handleActionClose()
{
    if (modelLoaded)
    {
        clearModel();
    }
    // Reset buttons to initial state (some buttons greyed out)
    setupButtons(modelLoaded);
    Q_EMIT statusUpdateMessage(QString("File closed"), 0);
}

void MainWindow::handleActionStlTest()
{
    //inputFileName = "tests/ExampleSTL.stl";
    std::string dataDir = DEFAULT_AO_DATA_DIR;
    inputFileName = QString::fromStdString(dataDir + "/ExampleSTL.stl");
    if (modelLoaded)
    {
        clearModel();
    }
    loadModel(inputFileName);
    // Q_EMIT statusUpdateMessage(QString("Loaded test STL model"), 0);
}

void MainWindow::handleActionModTest()
{
    //inputFileName = "tests/ExampleModel.mod";
    std::string dataDir = DEFAULT_AO_DATA_DIR;
    inputFileName = QString::fromStdString(dataDir + "/ExampleModel.mod");
    if (modelLoaded)
    {
        clearModel();
    }
    loadModel(inputFileName);
    // Q_EMIT statusUpdateMessage(QString("Loaded test MOD model"), 0);
}

void MainWindow::handleActionPrint()
{
    // Prompt the user for filename
    QString screenshotName = QFileDialog::getSaveFileName(this, tr("Save Image"), "", tr("Images (*.png)"));
    // Only export if a filename has been entered
    if (!screenshotName.isEmpty() && !screenshotName.isNull())
    {
        // Enable usage of alpha channel
        renderWindow->SetAlphaBitPlanes(1);
        ui->qvtkWidget->renderWindow()->Render();
        vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
        windowToImageFilter->SetInput(renderWindow);

        // Record the alpha (transparency channel)
        windowToImageFilter->SetInputBufferTypeToRGBA();
        windowToImageFilter->ReadFrontBufferOff();  // read from the back buffer
        windowToImageFilter->Update();

        screenshotName += ".png";

        // Convert QString
        vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
        std::string stdName = screenshotName.toStdString();
        const char* charName = stdName.c_str();

        // Write PNG
        writer->SetFileName(charName);
        writer->SetInputConnection(windowToImageFilter->GetOutputPort());
        writer->Write();

        Q_EMIT statusUpdateMessage(QString("Screenshot saved successfully"), 0);
    }
}

void MainWindow::handleActionFullScreen()
{
    if (this->isFullScreen())
    {
        this->showNormal();
    }
    else
    {
        this->showFullScreen();
    }
}

void MainWindow::handleActionAbout()
{
    HelpDialog* helpWindow = new HelpDialog();
    helpWindow->show();
}

void MainWindow::handleActionExportData()
{
    // Prompt user for a filename
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::currentPath(), tr("Text file (*.txt)"));

    // Only export if a filename has been entered
    if (!fileName.isEmpty() && !fileName.isNull())
    {
        fileName += ".txt";

        // Open the file
        QFile outFile(fileName);
        outFile.open(QIODevice::WriteOnly);

        // Print an error if file fails to open
        if (!outFile.isOpen())
        {
            QMessageBox messageBox;
            messageBox.critical(0, "Error", "Error while writing to the file");
            messageBox.setFixedSize(500, 200);
        }

        // Initialise output string
        QString outStr = "";

        outStr += "Volume: ";
        outStr += volumeString;
        outStr += "\n";

        outStr += "Surface area: ";
        outStr += surfAreaString;
        outStr += "\n";

        outStr += "Cells: ";
        outStr += cellString;
        outStr += "\n";

        outStr += "Points: ";
        outStr += pointString;
        outStr += "\n";

        // Initialise outStream
        QTextStream outStream(&outFile);

        // Write to stream
        outStream << outStr;

        // Close file
        outFile.close();

        Q_EMIT statusUpdateMessage(QString("Data exported successfully"), 0);
    }
}

void MainWindow::handleActionResetFilters()
{
    ui->resetFiltersButton->click();
}

void MainWindow::handleActionResetCamera()
{
    ui->resetCameraButton->click();
}

void MainWindow::handleActionResetProperties()
{
    ui->resetPropertiesButton->click();
}

void MainWindow::handleActionResetLighting()
{
    ui->resetLightingButton->click();
}

void MainWindow::on_shrinkButton_clicked()
{
    // Prompt user for shrink factor
    bool success;
    double shrinkFactor = QInputDialog::getDouble(this, tr("Input dialog"), tr("Insert shrink factor (0 to 1)"), 0.5, 0, 1, 2, &success);
    if (success)
    {
        // Connect shrink filter to STL reader
        vtkSmartPointer<vtkShrinkFilter> shrinkFilter = vtkSmartPointer<vtkShrinkFilter>::New();
        shrinkFilter->SetInputConnection(reader->GetOutputPort());
        shrinkFilter->SetShrinkFactor(shrinkFactor);
        shrinkFilter->Update();
        mappers[0]->SetInputConnection(shrinkFilter->GetOutputPort());

        actors[0]->SetMapper(mappers[0]);

        ui->qvtkWidget->renderWindow()->Render();
        ui->shrinkButton->setCheckable(true);
        Q_EMIT statusUpdateMessage(QString("Shrink filter enabled"), 0);
    }
    else
    {
        Q_EMIT statusUpdateMessage(QString("Error while enabling shrink filter"), 0);
    }
    ui->shrinkButton->setCheckable(true);
    ui->shrinkButton->setChecked(true);
}

void MainWindow::on_clipDialog_dialogAccepted()
{
    clipWindowShown = false;
}

void MainWindow::on_clipDialog_dialogRejected()
{
    // Set clip plane parameters to how they were before the dialog was opened
    clipX = prevClipX;
    clipY = prevClipY;
    clipZ = prevClipZ;
    clipNormalX = prevClipNormalX;
    clipNormalY = prevClipNormalY;
    clipNormalZ = prevClipNormalZ;

    clipPlane->SetOrigin(clipX, clipY, clipZ);

    clipWindowShown = false;
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Changes to clip filter cancelled"), 0);
}

void MainWindow::on_clipButton_clicked()
{
    // Toggle clip dialog between shown and hidden depending on its current state
    if (clipWindowShown)
    {
        clipWindow->close();
        clipWindowShown = false;
    }
    else
    {
        clipWindow->show();
        clipWindowShown = true;

        // Store current plane parameters in case dialog is rejected
        // (cancel button is pressed)
        prevClipX = clipX;
        prevClipY = clipY;
        prevClipZ = clipZ;
        prevClipNormalX = clipNormalX;
        prevClipNormalY = clipNormalY;
        prevClipNormalZ = clipNormalZ;

        // When clicked for the first time, initialise clip plane with these parameters
        if (!clipFilterEnabled)
            clipFilter = NULL;
        clipPlane = NULL;
        // Connect clip filter to STL reader
        clipFilter = vtkSmartPointer<vtkClipDataSet>::New();
        clipPlane = vtkSmartPointer<vtkPlane>::New();
        clipFilter->SetInputConnection(reader->GetOutputPort());
        clipPlane->SetOrigin(0.0, 0.0, 0.0);
        clipPlane->SetNormal(-1.0, 0.0, 0.0);
        clipFilter->SetClipFunction(clipPlane.Get());
        mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
        actors[0]->SetMapper(mappers[0]);

        ui->qvtkWidget->renderWindow()->Render();
        ui->clipButton->setCheckable(true);
        ui->clipButton->setChecked(true);
        Q_EMIT statusUpdateMessage(QString("Clip filter enabled"), 0);
        clipFilterEnabled = true;
    }
    ui->clipButton->setCheckable(true);
    ui->clipButton->setChecked(true);
}

void MainWindow::on_clipXSlider_sliderMoved(int position)
{
    if (position == 99)
    {
        clipX = 10;
    }
    else
    {
        clipX = -(float)position * 10;
    }
    clipPlane->SetOrigin(clipX, clipY, clipZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("X parameter of clip filter changed"), 0);
}

void MainWindow::on_clipYSlider_sliderMoved(int position)
{
    if (position == 99)
    {
        clipY = 20;
    }
    else
    {
        clipY = (float)position * 20;
    }
    clipPlane->SetOrigin(clipX, clipY, clipZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Y parameter of clip filter changed"), 0);
}

void MainWindow::on_clipZSlider_sliderMoved(int position)
{
    if (position == 99)
    {
        clipZ = 20;
    }
    else
    {
        clipZ = (float)position * 20;
    }
    clipPlane->SetOrigin(clipX, clipY, clipZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Z parameter of clip filter changed"), 0);
}

void MainWindow::on_clipXDial_sliderMoved(int position)
{
    if (position == 99)
    {
        clipNormalX = 10;
    }
    else
    {
        clipNormalX = (float)position * 10;
    }
    clipPlane->SetNormal(clipNormalX, clipNormalY, clipNormalZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("X rotation of clip filter changed"), 0);
}

void MainWindow::on_clipYDial_sliderMoved(int position)
{
    if (position == 99)
    {
        clipNormalY = 10;
    }
    else
    {
        clipNormalY = (float)position * 10;
    }
    clipPlane->SetNormal(clipNormalX, clipNormalY, clipNormalZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Y rotation of clip filter changed"), 0);
}

void MainWindow::on_clipZDial_sliderMoved(int position)
{
    if (position == 99)
    {
        clipNormalZ = 10;
    }
    else
    {
        clipNormalZ = (float)position * 10;
    }
    clipPlane->SetNormal(clipNormalX, clipNormalY, clipNormalZ);
    clipFilter->SetClipFunction(clipPlane.Get());
    mappers[0]->SetInputConnection(clipFilter->GetOutputPort());
    actors[0]->SetMapper(mappers[0]);
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Z rotation of clip filter changed"), 0);
}

void MainWindow::on_bkgColourButton_clicked()
{
    // Prompt user for colour
    QColor rgbColours = QColorDialog::getColor(Qt::white, this, "Choose Background Colour");
    double r = rgbColours.redF();
    double g = rgbColours.greenF();
    double b = rgbColours.blueF();

    // Check that colour is valid, otherwise show an error
    if (rgbColours.isValid())
    {
        renderer->SetBackground(r, g, b);
        ui->qvtkWidget->renderWindow()->Render();
    }
    else
    {
        Q_EMIT statusUpdateMessage(QString("Errour while changing background colour"), 0);
    }
    Q_EMIT statusUpdateMessage(QString("Background colour changed"), 0);
}

void MainWindow::on_modColourButton_clicked()
{
    // Prompt user for colour
    QColor rgbColours = QColorDialog::getColor(Qt::white, this, "Choose Model Colour");
    double r = rgbColours.redF();
    double g = rgbColours.greenF();
    double b = rgbColours.blueF();

    // Check that colour is valid, otherwise show an error
    if (rgbColours.isValid())
    {
        // Ensure all actors are properly coloured in case it's a .mod file
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetColor(r, g, b);
        }
        ui->qvtkWidget->renderWindow()->Render();
    }
    else
    {
        Q_EMIT statusUpdateMessage(QString("Error while changing model colour"), 0);
    }
    Q_EMIT statusUpdateMessage(QString("Model colour changed"), 0);
}

void MainWindow::on_resetCameraButton_clicked()
{
    resetCamera();
    ui->qvtkWidget->renderWindow()->Render();
    Q_EMIT statusUpdateMessage(QString("Camera has been reset"), 0);
}

void MainWindow::on_resetPropertiesButton_clicked()
{
    if (modelLoaded)
    {
        clearModel();
    }

    // Recolor model
    loadModel(inputFileName);

    // Set default background colour
    renderer->GradientBackgroundOn();
    renderer->SetBackground(colors->GetColor3d("Black").GetData());
    renderer->SetBackground2(0, 0.6, 0.5);

    Q_EMIT statusUpdateMessage(QString("Properties have been reset"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_resetLightingButton_clicked()
{
    // Set maximum opacity
    ui->opacitySlider->setValue(99);
    for (int i = 0; i < actors.size(); i++)
    {
        actors[i]->GetProperty()->SetOpacity(1);
    }

    // Remove external light
    ui->intensityCheckBox->setChecked(false);
    renderer->RemoveAllLights();
    ui->intensitySlider->setValue(49);
    ui->intensitySlider->setEnabled(false);

    // Set specularity to half value
    ui->specularitySlider->setValue(49);
    for (int i = 0; i < actors.size(); i++)
    {
        actors[i]->GetProperty()->SetSpecular(0.5);
    }

    Q_EMIT statusUpdateMessage(QString("Lighting has been reset"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_resetFiltersButton_clicked()
{
    if (modelLoaded)
    {
        clearModel();
    }

    // Recolor model
    loadModel(inputFileName);

    // Uncheck buttons
    ui->shrinkButton->setChecked(false);
    ui->clipButton->setChecked(false);

    clipFilterEnabled = false;

    clipWindow->hide();

    clipX = 0;
    clipY = 0;
    clipZ = 0;
    clipNormalX = -1;
    clipNormalY = 0;
    clipNormalZ = 0;

    Q_EMIT statusUpdateMessage(QString("Filters have been reset"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_posXButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(0, 0, 0);
    renderer->GetActiveCamera()->SetPosition(1, 0, 0);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_posYButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(1, 0, 0);
    renderer->GetActiveCamera()->SetPosition(0, 1, 0);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_posZButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(0, 1, 0);
    renderer->GetActiveCamera()->SetPosition(0, 0, 1);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_pos90Button_clicked()
{
    renderer->GetActiveCamera()->Roll(-90);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_negXButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(0, 1, 0);
    renderer->GetActiveCamera()->SetPosition(-1, 0, 0);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_negYButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(1, 0, 0);
    renderer->GetActiveCamera()->SetPosition(0, -1, 0);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_negZButton_clicked()
{
    renderer->GetActiveCamera()->SetFocalPoint(0.0, 0.0, 0.0);
    renderer->GetActiveCamera()->SetViewUp(0, 1, 0);
    renderer->GetActiveCamera()->SetPosition(0, 0, -1);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_neg90Button_clicked()
{
    renderer->GetActiveCamera()->Roll(90);

    // Re-render scene
    renderer->ResetCamera();
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::handleActionEnableIntensity()
{
    Qt::CheckState state = ui->intensityCheckBox->checkState();
    if (state == Qt::Unchecked)
    {
        ui->intensityCheckBox->setCheckState(Qt::Checked);
    }
    else if (state == Qt::Checked)
    {
        ui->intensityCheckBox->setCheckState(Qt::Unchecked);
    }
}

void MainWindow::handleActionShowAxes()
{
    Qt::CheckState state = ui->showAxesCheckBox->checkState();
    if (state == Qt::Unchecked)
    {
        ui->showAxesCheckBox->setCheckState(Qt::Checked);
    }
    else if (state == Qt::Checked)
    {
        ui->showAxesCheckBox->setCheckState(Qt::Unchecked);
    }
}

void MainWindow::on_gradientCheckBox_stateChanged(int state)
{
    // Note: state == 1 means partially checked
    if (state == 0)  // unchecked
    {
        renderer->GradientBackgroundOff();
        Q_EMIT statusUpdateMessage(QString("Gradient background disabled"), 0);
    }
    else if (state == 2)  // checked
    {
        double r, g, b;
        renderer->GetBackground(r, g, b);
        renderer->GradientBackgroundOn();
        renderer->SetBackground(r, g, b);
        renderer->SetBackground2(0, 0.6, 0.5);
        Q_EMIT statusUpdateMessage(QString("Gradient background enabled"), 0);
    }
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_intensityCheckBox_stateChanged(int state)
{
    // Note: state == 1 means partially checked
    if (state == 0)  // unchecked
    {
        ui->intensitySlider->setEnabled(false);
        renderer->RemoveAllLights();
        Q_EMIT statusUpdateMessage(QString("External light disabled"), 0);
    }
    else if (state == 2)  // checked
    {
        renderer->AddLight(light);
        ui->intensitySlider->setEnabled(true);
        Q_EMIT statusUpdateMessage(QString("External light enabled"), 0);
    }
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_showAxesCheckBox_stateChanged(int state)
{
    // Note: state == 1 means partially checked
    if (state == 0)  // unchecked
    {
        renderer->RemoveActor(axes);
        Q_EMIT statusUpdateMessage(QString("Coordinate axes disabled"), 0);
    }
    else if (state == 2)  // checked
    {
        renderer->AddActor(axes);
        Q_EMIT statusUpdateMessage(QString("Coordinate axes enabled"), 0);
    }
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_wireframeRadio_toggled(bool checked)
{
    if (checked)
    {
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetRepresentationToWireframe();
        }
    }
    Q_EMIT statusUpdateMessage(QString("Wireframe visualization enabled"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_pointsRadio_toggled(bool checked)
{
    if (checked)
    {
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetRepresentationToPoints();
        }
    }
    Q_EMIT statusUpdateMessage(QString("Points visualization enabled"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_surfaceRadio_toggled(bool checked)
{
    if (checked)
    {
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetRepresentationToSurface();
        }
    }
    Q_EMIT statusUpdateMessage(QString("Surface visualization enabled"), 0);
    ui->qvtkWidget->renderWindow()->Render();
}

void MainWindow::on_opacitySlider_sliderMoved(int position)
{
    float pos;
    if (position == 99)
    {
        pos = 1;
    }
    else
    {
        pos = (float)position / 100;
    }

    if (actors.size() == 1)
    {
        actors[0]->GetProperty()->SetOpacity(pos);
        ui->qvtkWidget->renderWindow()->Render();
    }
}

void MainWindow::on_opacitySlider_valueChanged(int value)
{
    float pos;
    if (value == 99)
    {
        pos = 1;
    }
    else
    {
        pos = (float)value / 100;
    }

    if (actors.size() != 1)
    {
        // Ensure all actors are properly coloured in case it's a .mod file
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetOpacity(pos);
        }
        ui->qvtkWidget->renderWindow()->Render();
    }
}

void MainWindow::on_specularitySlider_sliderMoved(int position)
{
    float pos;
    if (position == 99)
    {
        pos = 1;
    }
    else
    {
        pos = (float)position / 100;
    }

    if (actors.size() == 1)
    {
        actors[0]->GetProperty()->SetSpecular(pos);

        ui->qvtkWidget->renderWindow()->Render();
    }
}

void MainWindow::on_specularitySlider_valueChanged(int value)
{
    float pos;
    if (value == 99)
    {
        pos = 1;
    }
    else
    {
        pos = (float)value / 100;
    }

    if (actors.size() != 1)
    {
        // Ensure all actors are properly coloured in case it's a .mod file
        for (int i = 0; i < actors.size(); i++)
        {
            actors[i]->GetProperty()->SetSpecular(pos);
        }
        ui->qvtkWidget->renderWindow()->Render();
    }
}

void MainWindow::on_intensitySlider_sliderMoved(int position)
{
    float pos;
    if (position == 99)
    {
        pos = 1;
    }
    else
    {
        pos = (float)position / 100;
    }

    light->SetIntensity(pos);
    ui->qvtkWidget->renderWindow()->Render();
}